/*

   Copyright 2010 Trevor Hogan

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

#ifndef BNETPROTOCOL_H
#define BNETPROTOCOL_H

#include "data/Friend.h"

#include <string>

using namespace std;

//
// CBNETProtocol
//

#define BNET_HEADER_CONSTANT 255

class CIncomingGameHost;
class CIncomingChatEvent;

class CBNETProtocol
{
public:

    enum Protocol
    {
        SID_NULL = 0,                           // 0x0
        SID_STOPADV = 2,                        // 0x2
        SID_GETADVLISTEX = 9,                   // 0x9
        SID_ENTERCHAT = 10,                     // 0xA
        SID_JOINCHANNEL = 12,                   // 0xC
        SID_CHATCOMMAND = 14,                   // 0xE
        SID_CHATEVENT = 15,                     // 0xF
        SID_CHECKAD = 21,                       // 0x15
        SID_MESSAGEBOX = 25,                    // 0x19
        SID_STARTADVEX3 = 28,                   // 0x1C
        SID_DISPLAYAD = 33,                     // 0x21
        SID_NOTIFYJOIN = 34,                    // 0x22
        SID_PING = 37,                          // 0x25
        SID_LOGONRESPONSE = 41,                 // 0x29
        SID_NETGAMEPORT = 69,                   // 0x45
        SID_AUTH_INFO = 80,                     // 0x50
        SID_AUTH_CHECK = 81,                    // 0x51
        SID_AUTH_ACCOUNTLOGON = 83,             // 0x53
        SID_AUTH_ACCOUNTLOGONPROOF = 84,        // 0x54
        SID_WARDEN = 94,                        // 0x5E
        SID_FRIENDSLIST = 101,                  // 0x65
        SID_FRIENDSUPDATE = 102,                // 0x66
        SID_FRIENDSADD = 103,                   // 0x67
        SID_FRIENDSREMOVE = 104,                // 0x68
        SID_FRIENDSPOSITION = 105,              // 0x69
        SID_CLANMEMBERLIST = 125,               // 0x7D
        SID_CLANMEMBERSTATUSCHANGE = 127        // 0x7F
    };

    enum KeyResult
    {
        KR_GOOD = 0,
        KR_OLD_GAME_VERSION = 256,
        KR_INVALID_VERSION = 257,
        KR_ROC_KEY_IN_USE = 513,
        KR_TFT_KEY_IN_USE = 529
    };

    enum IncomingChatEvent
    {
        EID_SHOWUSER = 1,               // received when you join a channel (includes users in the channel and their information)
        EID_JOIN = 2,                   // received when someone joins the channel you're currently in
        EID_LEAVE = 3,                  // received when someone leaves the channel you're currently in
        EID_WHISPER = 4,                // received a whisper message
        EID_TALK = 5,                   // received when someone talks in the channel you're currently in
        EID_BROADCAST = 6,              // server broadcast
        EID_CHANNEL = 7,                // received when you join a channel (includes the channel's name, flags)
        EID_USERFLAGS = 9,              // user flags updates
        EID_WHISPERSENT = 10,           // sent a whisper message
        EID_CHANNELFULL = 13,           // channel is full
        EID_CHANNELDOESNOTEXIST = 14,   // channel does not exist
        EID_CHANNELRESTRICTED = 15,     // channel is restricted
        EID_INFO = 18,                  // broadcast/information message
        EID_ERROR = 19,                 // error message
        EID_EMOTE = 23                  // emote
    };

    enum JoinChannelFlags
    {
        JCF_NOCREATE_JOIN = 0,
        JCF_FIRST_JOIN = 1,
        JCF_FORCED_JOIN = 2
    };

private:
    BYTEARRAY m_ClientToken;            // set in constructor
    BYTEARRAY m_LogonType;              // set in RECEIVE_SID_AUTH_INFO
    BYTEARRAY m_ServerToken;            // set in RECEIVE_SID_AUTH_INFO
    BYTEARRAY m_MPQFileTime;            // set in RECEIVE_SID_AUTH_INFO
    BYTEARRAY m_IX86VerFileName;        // set in RECEIVE_SID_AUTH_INFO
    BYTEARRAY m_ValueStringFormula;     // set in RECEIVE_SID_AUTH_INFO
    BYTEARRAY m_KeyState;               // set in RECEIVE_SID_AUTH_CHECK
    BYTEARRAY m_KeyStateDescription;    // set in RECEIVE_SID_AUTH_CHECK
    BYTEARRAY m_Salt;                   // set in RECEIVE_SID_AUTH_ACCOUNTLOGON
    BYTEARRAY m_ServerPublicKey;        // set in RECEIVE_SID_AUTH_ACCOUNTLOGON
    BYTEARRAY m_UniqueName;             // set in RECEIVE_SID_ENTERCHAT

public:
    CBNETProtocol();
    ~CBNETProtocol();

    BYTEARRAY GetClientToken()
    {
        return m_ClientToken;
    }

    BYTEARRAY GetLogonType()
    {
        return m_LogonType;
    }

    BYTEARRAY GetServerToken()
    {
        return m_ServerToken;
    }

    BYTEARRAY GetMPQFileTime()
    {
        return m_MPQFileTime;
    }

    BYTEARRAY GetIX86VerFileName()
    {
        return m_IX86VerFileName;
    }

    string GetIX86VerFileNameString()
    {
        return string(m_IX86VerFileName.begin(), m_IX86VerFileName.end());
    }

    BYTEARRAY GetValueStringFormula()
    {
        return m_ValueStringFormula;
    }

    string GetValueStringFormulaString()
    {
        return string(m_ValueStringFormula.begin(), m_ValueStringFormula.end());
    }

    BYTEARRAY GetKeyState()
    {
        return m_KeyState;
    }

    string GetKeyStateDescription()
    {
        return string(m_KeyStateDescription.begin(), m_KeyStateDescription.end());
    }

    BYTEARRAY GetSalt()
    {
        return m_Salt;
    }

    BYTEARRAY GetServerPublicKey()
    {
        return m_ServerPublicKey;
    }

    BYTEARRAY GetUniqueName()
    {
        return m_UniqueName;
    }

    // receive functions

    bool RECEIVE_SID_NULL(BYTEARRAY data);
    vector<CIncomingGameHost *> RECEIVE_SID_GETADVLISTEX(BYTEARRAY data);
    bool RECEIVE_SID_ENTERCHAT(BYTEARRAY data);
    CIncomingChatEvent *RECEIVE_SID_CHATEVENT(BYTEARRAY data);
    bool RECEIVE_SID_CHECKAD(BYTEARRAY data);
    bool RECEIVE_SID_STARTADVEX3(BYTEARRAY data);
    BYTEARRAY RECEIVE_SID_PING(BYTEARRAY data);
    bool RECEIVE_SID_LOGONRESPONSE(BYTEARRAY data);
    bool RECEIVE_SID_AUTH_INFO(BYTEARRAY data);
    bool RECEIVE_SID_AUTH_CHECK(BYTEARRAY data);
    bool RECEIVE_SID_AUTH_ACCOUNTLOGON(BYTEARRAY data);
    bool RECEIVE_SID_AUTH_ACCOUNTLOGONPROOF(BYTEARRAY data);
    BYTEARRAY RECEIVE_SID_WARDEN(BYTEARRAY data);
    QList<Friend*> RECEIVE_SID_FRIENDSLIST(BYTEARRAY data);
    Friend* RECEIVE_SID_FRIENDSUPDATE(BYTEARRAY data);
    Friend* RECEIVE_SID_FRIENDSADD(BYTEARRAY data);
    unsigned char RECEIVE_SID_FRIENDSREMOVE(BYTEARRAY data);
    // Won't happen until the client (GProxy) supports position changing.
    // void RECEIVE_SID_FRIENDSPOSITION(BYTEARRAY data);
    BYTEARRAY RECEIVE_SID_MESSAGEBOX(BYTEARRAY data);

    // send functions

    BYTEARRAY SEND_PROTOCOL_INITIALIZE_SELECTOR();
    BYTEARRAY SEND_SID_NULL();
    BYTEARRAY SEND_SID_STOPADV();
    BYTEARRAY SEND_SID_GETADVLISTEX(string gameName, uint32_t numGames); //[20]
    BYTEARRAY SEND_SID_ENTERCHAT();
    BYTEARRAY SEND_SID_JOINCHANNEL(CBNETProtocol::JoinChannelFlags flag, string channel);
    BYTEARRAY SEND_SID_CHATCOMMAND(QString command);
    BYTEARRAY SEND_SID_CHECKAD();
    BYTEARRAY SEND_SID_STARTADVEX3(unsigned char state, BYTEARRAY mapGameType, BYTEARRAY mapFlags, BYTEARRAY mapWidth, BYTEARRAY mapHeight, string gameName, string hostName, uint32_t upTime, string mapPath, BYTEARRAY mapCRC, BYTEARRAY mapSHA1, uint32_t hostCounter);
    BYTEARRAY SEND_SID_NOTIFYJOIN(string gameName);
    BYTEARRAY SEND_SID_PING(BYTEARRAY pingValue);
    BYTEARRAY SEND_SID_LOGONRESPONSE(BYTEARRAY clientToken, BYTEARRAY serverToken, BYTEARRAY passwordHash, string accountName);
    BYTEARRAY SEND_SID_NETGAMEPORT(uint16_t serverPort);
    BYTEARRAY SEND_SID_AUTH_INFO(unsigned int ver, bool TFT, string countryAbbrev, string country);
    BYTEARRAY SEND_SID_AUTH_CHECK(bool TFT, BYTEARRAY clientToken, BYTEARRAY exeVersion, BYTEARRAY exeVersionHash, BYTEARRAY keyInfoROC, BYTEARRAY keyInfoTFT, string exeInfo, string keyOwnerName);
    BYTEARRAY SEND_SID_AUTH_ACCOUNTLOGON(BYTEARRAY clientPublicKey, string accountName);
    BYTEARRAY SEND_SID_AUTH_ACCOUNTLOGONPROOF(BYTEARRAY clientPasswordProof);
    BYTEARRAY SEND_SID_WARDEN(BYTEARRAY wardenResponse);
    BYTEARRAY SEND_SID_FRIENDSLIST();
    BYTEARRAY SEND_SID_FRIENDSUPDATE(unsigned char entryNumber);
    BYTEARRAY SEND_SID_CLANMEMBERLIST();

    // other functions

private:
    bool AssignLength(BYTEARRAY &content);
    bool ValidateLength(BYTEARRAY &content);
};

//
// CIncomingGameHost
//

class CIncomingGameHost
{
public:
    static uint32_t NextUniqueGameID;

private:
    uint16_t m_GameType;
    uint16_t m_Parameter;
    uint32_t m_LanguageID;
    uint16_t m_Port;
    BYTEARRAY m_IP;
    uint32_t m_Status;
    uint32_t m_ElapsedTime;
    string m_GameName;
    unsigned char m_SlotsTotal;
    uint32_t m_HostCounter;
    BYTEARRAY m_StatString;
    uint32_t m_UniqueGameID;
    uint32_t m_ReceivedTime;

    // decoded from stat string:

    uint32_t m_MapFlags;
    uint16_t m_MapWidth;
    uint16_t m_MapHeight;
    BYTEARRAY m_MapCRC;
    string m_MapPath;
    string m_HostName;

public:
    CIncomingGameHost(uint16_t nGameType, uint16_t nParameter, uint32_t nLanguageID, uint16_t nPort, BYTEARRAY &nIP, uint32_t nStatus, uint32_t nElapsedTime, string nGameName, unsigned char nSlotsTotal, uint32_t nHostCounter, BYTEARRAY &nStatString);
    ~CIncomingGameHost();

    uint16_t GetGameType()
    {
        return m_GameType;
    }

    uint16_t GetParameter()
    {
        return m_Parameter;
    }

    uint32_t GetLanguageID()
    {
        return m_LanguageID;
    }

    uint16_t GetPort()
    {
        return m_Port;
    }

    BYTEARRAY GetIP()
    {
        return m_IP;
    }
    string GetIPString();

    uint32_t GetStatus()
    {
        return m_Status;
    }

    uint32_t GetElapsedTime()
    {
        return m_ElapsedTime;
    }

    string GetGameName()
    {
        return m_GameName;
    }

    unsigned char GetSlotsTotal()
    {
        return m_SlotsTotal;
    }

    uint32_t GetHostCounter()
    {
        return m_HostCounter;
    }

    BYTEARRAY GetStatString()
    {
        return m_StatString;
    }

    uint32_t GetUniqueGameID()
    {
        return m_UniqueGameID;
    }

    uint32_t GetReceivedTime()
    {
        return m_ReceivedTime;
    }

    uint32_t GetMapFlags()
    {
        return m_MapFlags;
    }

    uint16_t GetMapWidth()
    {
        return m_MapWidth;
    }

    uint16_t GetMapHeight()
    {
        return m_MapHeight;
    }

    BYTEARRAY GetMapCRC()
    {
        return m_MapCRC;
    }

    string GetMapPath()
    {
        return m_MapPath;
    }

    string GetHostName()
    {
        return m_HostName;
    }
};

//
// CIncomingChatEvent
//

class CIncomingChatEvent
{
private:
    CBNETProtocol::IncomingChatEvent m_ChatEvent;

    QString m_User;
    QString m_Message;
    uint32_t m_Ping;

public:

    uint32_t GetPingPhy(); //phy
    CIncomingChatEvent(CBNETProtocol::IncomingChatEvent nChatEvent, uint32_t nPing, string nUser, QString nMessage);
    ~CIncomingChatEvent();

    CBNETProtocol::IncomingChatEvent GetChatEvent()
    {
        return m_ChatEvent;
    }

    uint32_t GetPing()
    {
        return m_Ping;
    }

    QString GetUser()
    {
        return m_User;
    }

    QString GetMessage()
    {
        return m_Message;
    }
};

//
// CIncomingClanList
//

class CIncomingClanList
{
private:
    string m_Name;
    unsigned char m_Rank;
    unsigned char m_Status;

public:
    CIncomingClanList(string nName, unsigned char nRank, unsigned char nStatus);
    ~CIncomingClanList();

    string GetName()
    {
        return m_Name;
    }
    string GetRank();
    string GetStatus();
    string GetDescription();
};

#endif
